﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.CodeDom.Compiler;
using Microsoft.VisualStudio.TextTemplating;
using System.Data;
using System.Data.SqlClient;

namespace CodeGenerator
{
    /// <summary>
    /// 模版宿主
    /// </summary>
    [Serializable]
    public class TemplateHost : ITextTemplatingEngineHost
    {
        #region 字段
        private CompilerErrorCollection _ErrorCollection;
        private Encoding _fileEncodingValue = Encoding.UTF8;
        private string _fileExtensionValue = ".cs";
        private string _namespace = "CodeGenerator";
        internal string _templateFileValue;
        #endregion

        #region 属性
        /// <summary>
        /// 编译错误对象集合
        /// </summary>
        public CompilerErrorCollection ErrorCollection
        {
            get { return this._ErrorCollection; }
        }

        /// <summary>
        /// 文件编码方式
        /// </summary>
        public Encoding FileEncoding
        {
            get { return this._fileEncodingValue; }
        }

        /// <summary>
        /// 文件扩展名
        /// </summary>
        public string FileExtension
        {
            get { return this._fileExtensionValue; }
        }

        /// <summary>
        /// 宿主所在命名空间
        /// </summary>
        public string NameSpace
        {
            get { return this._namespace; }
            set { this._namespace = value; }
        }

        /// <summary>
        /// 模版需调用的其他程序集引用
        /// </summary>
        public IList<string> StandardAssemblyReferences
        {
            get
            {
                return new string[] { 
            typeof(Uri).Assembly.Location,
            typeof(HostParam).Assembly.Location,
            typeof(SchemaInfo).Assembly.Location,
            typeof(TemplateHost).Assembly.Location 
        };
            }
        }

        /// <summary>
        /// 模版调用标准程序集引用
        /// </summary>
        public IList<string> StandardImports
        {
            get
            {
                return new string[] { 
            "System", 
            "System.Text", 
            "System.Collections.Generic",
            "CodeGenerator"
        };
            }
        }

        /// <summary>
        /// 模版文件
        /// </summary>
        public string TemplateFile
        {
            get { return this._templateFileValue; }
            set { this._templateFileValue = value; }
        }

        /// <summary>
        /// 自定义参数对象用于向模板传参
        /// </summary>
        public HostParam Param { get; set; }
        #endregion

        #region 方法
        public object GetHostOption(string optionName)
        {
            string str;
            return (((str = optionName) != null) && (str == "CacheAssemblies"));
        }

        public bool LoadIncludeText(string requestFileName, out string content, out string location)
        {
            content = string.Empty;
            location = string.Empty;
            if (File.Exists(requestFileName))
            {
                content = File.ReadAllText(requestFileName);
                return true;
            }
            return false;
        }

        public void LogErrors(CompilerErrorCollection errors)
        {
            this._ErrorCollection = errors;
        }

        public AppDomain ProvideTemplatingAppDomain(string content)
        {
            return AppDomain.CreateDomain("Generation App Domain");
        }

        public string ResolveAssemblyReference(string assemblyReference)
        {
            if (File.Exists(assemblyReference))
            {
                return assemblyReference;
            }
            string path = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference);
            if (File.Exists(path))
            {
                return path;
            }
            return "";
        }

        public Type ResolveDirectiveProcessor(string processorName)
        {
            string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase);
            throw new Exception("没有找到指令处理器");
        }

        public string ResolveParameterValue(string directiveId, string processorName, string parameterName)
        {
            if (directiveId == null)
            {
                throw new ArgumentNullException("the directiveId cannot be null");
            }
            if (processorName == null)
            {
                throw new ArgumentNullException("the processorName cannot be null");
            }
            if (parameterName == null)
            {
                throw new ArgumentNullException("the parameterName cannot be null");
            }
            return string.Empty;
        }

        public string ResolvePath(string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException("the file name cannot be null");
            }
            if (!File.Exists(fileName))
            {
                string path = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName);
                if (File.Exists(path))
                {
                    return path;
                }
            }
            return fileName;
        }

        public void SetFileExtension(string extension)
        {
            this._fileExtensionValue = extension;
        }

        public void SetOutputEncoding(Encoding encoding, bool fromOutputDirective)
        {
            this._fileEncodingValue = encoding;
        }
        #endregion
    }


    /// <summary>
    /// 参数对象
    /// </summary>
    [Serializable]
    public class HostParam
    {
        /// <summary>
        /// 数据表名称
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        /// 命名空间
        /// </summary>
        public string NameSpace { get; set; }

        /// <summary>
        /// 数据表列集合
        /// </summary>
        public List<SchemaInfo> ColumnList { get; set; }
        /// <summary>
        /// 生成文件的类别
        /// </summary>
        public string Type { get; set; }
    }

    #region DbTable
    /// <summary>
    /// 表结构
    /// </summary>
    public sealed class DbTable
    {
        /// <summary>
        /// 表名称
        /// </summary>
        public string TableName { get; set; }
        /// <summary>
        /// 表的架构
        /// </summary>
        public string SchemaName { get; set; }
        /// <summary>
        /// 表的记录数
        /// </summary>
        public int Rows { get; set; }

        /// <summary>
        /// 是否含有主键
        /// </summary>
        public bool HasPrimaryKey { get; set; }
    }
    #endregion

    /// <summary>
    /// 表结构信息
    /// </summary>
    [Serializable]
    public class SchemaInfo
    {
        /// <summary>
        /// 列描述信息
        /// </summary>
        public string ColumnDesc { get; set; }

        /// <summary>
        /// 列数据类型
        /// </summary>
        public string ColumnType { get; set; }

        /// <summary>
        /// 列名称
        /// </summary>
        public string ColumnName { get; set; }
    }


    //数据库操作
    public static class DBHelper
    {
        private static SqlCommand PrepareCommand(SqlConnection conn, string cmdText, CommandType cmdType, SqlParameter[] parameters)
        {
            if (conn.State != ConnectionState.Open)
            {
                conn.Open();
            }
            SqlCommand cmd = new SqlCommand(cmdText, conn);
            cmd.CommandType = cmdType;
            if (parameters != null)
            {
                cmd.Parameters.Clear();
                cmd.Parameters.AddRange(parameters);
            }

            return cmd;
        }

        public static DataTable GetDataTable(string connString, string strSQL)
        {
            using (SqlConnection conn = new SqlConnection(connString))
            {
                SqlDataAdapter da = new SqlDataAdapter(strSQL, conn);
                DataTable dt = new DataTable();
                da.Fill(dt);
                return dt;
            }
        }
        public static List<DbTable> GetTables(string connString,string database,string tableName,bool ishaveV=false)
        {
            string type = "'U'";
            if (ishaveV)
            {
                type += ",'V'";
            }
            if (string.IsNullOrEmpty(tableName)) { tableName = "%"; }
            #region SQL
            string sql = string.Format(@"SELECT
									obj.name tablename,
									schem.name schemname,
									CAST
									(
										CASE 
											WHEN (SELECT COUNT(1) FROM sys.indexes WHERE object_id= obj.OBJECT_ID AND is_primary_key=1) >=1 THEN 1
											ELSE 0
										END 
									AS BIT) HasPrimaryKey                                         
									from {0}.sys.objects obj 
									INNER JOIN {0}.sys.schemas schem ON obj.schema_id=schem.schema_id
									where type in({2}) and obj.name like '{1}'
									order by obj.name", database,tableName,type);
            #endregion
            DataTable dt = GetDataTable(connString, sql);
            List<DbTable> list = new List<DbTable>();
            foreach (DataRow dr in dt.Rows)
            {
                DbTable info = new DbTable();
                info.TableName = dr["tablename"] + "";
                info.SchemaName = dr["schemname"] + "";
                list.Add(info);
            }
            return list;
        }
        public static List<SchemaInfo> GetSchema(string connString, string tableName,bool ishaveV = false)
        {
            string type = "'U'";
            if (ishaveV)
            {
                type += ",'V'";
            }
            StringBuilder sbSQL = new StringBuilder();
            sbSQL.AppendLine("SELECT ");
            sbSQL.AppendLine("    [Id]=C.column_id,");
            sbSQL.AppendLine("    [Name]=C.name,");
            sbSQL.AppendLine("    [Type]=T.name,");
            sbSQL.AppendLine("    [Length]=C.max_length,");
            sbSQL.AppendLine("    [Identity]=CASE WHEN C.is_identity=1 THEN N'T'ELSE N'' END,");
            sbSQL.AppendLine("    [PrimaryKey]=ISNULL(PKInfo.PrimaryKey,N''),");
            sbSQL.AppendLine("    [ForeignKey]=CASE WHEN FKInfo.parent_column_id>0 THEN N'T'ELSE N'' END,");
            sbSQL.AppendLine("    [ForeignKeyTable]=ISNULL(FKInfo.name,N''),");
            sbSQL.AppendLine("    [IsNull]=CASE WHEN C.is_nullable=1 THEN N'T'ELSE N'' END,");
            sbSQL.AppendLine("    [Default]=ISNULL(DC.definition,N''),");
            sbSQL.AppendLine("    [ColumnDesc]=ISNULL(EP.value,N'') ");
            sbSQL.AppendLine("FROM sys.columns C ");
            sbSQL.AppendLine(string.Format("INNER JOIN sys.objects O ON C.object_id=o.object_id AND O.type in({0}) AND O.is_ms_shipped=0 ",type));
            sbSQL.AppendLine("INNER JOIN sys.types T ON C.user_type_id=T.user_type_id ");
            sbSQL.AppendLine("LEFT JOIN sys.default_constraints DC ON C.object_id=DC.parent_object_id AND C.column_id=DC.parent_column_id AND C.default_object_id=DC.object_id ");
            sbSQL.AppendLine("LEFT JOIN sys.extended_properties EP ON EP.class=1 AND C.object_id=EP.major_id AND C.column_id=EP.minor_id ");
            sbSQL.AppendLine("LEFT JOIN (SELECT IC.object_id,IC.column_id,PrimaryKey=CASE WHEN I.is_primary_key=1 THEN N'T'ELSE N'' END FROM sys.indexes I INNER JOIN sys.index_columns IC ON I.[object_id]=IC.[object_id] AND I.index_id=IC.index_id)PKInfo ON PKInfo.object_id=C.object_id AND PKInfo.column_id=C.column_id ");
            sbSQL.AppendLine("LEFT JOIN (SELECT FKC.parent_object_id,FKC.parent_column_id,O.name FROM sys.foreign_key_columns FKC INNER JOIN sys.objects O ON FKC.referenced_object_id=O.object_id)FKInfo ON C.object_id=FKInfo.parent_object_id AND C.column_id=FKInfo.parent_column_id ");
            sbSQL.AppendFormat("WHERE O.name='{0}' ORDER BY Id ASC", tableName,type);
            DataTable dt = GetDataTable(connString, sbSQL.ToString());
            List<SchemaInfo> list = new List<SchemaInfo>();
            foreach (DataRow dr in dt.Rows)
            {
                SchemaInfo info = new SchemaInfo();
                info.ColumnDesc = dr["ColumnDesc"].ToString();
                info.ColumnName = dr["Name"].ToString();
                info.ColumnType = TypeConvertor.MapType(dr["Type"].ToString());
                list.Add(info);
            }
            return list;
        }
    }

    //类型转换
    public static class TypeConvertor
    {
        //将数据库类型映射成C#类型
        public static string MapType(string dbType)
        {
            if (string.IsNullOrEmpty(dbType)) return dbType;
            dbType = dbType.ToLower();
            string csType = "object";
            switch (dbType)
            {
                case "char": csType = "string"; break;
                case "date": csType = "DateTime"; break;
                case "datetime": csType = "DateTime"; break;
                case "decimal": csType = "decimal"; break;
                case "float": csType = "double"; break;
                case "int": csType = "int"; break;
                case "money": csType = "decimal"; break;
                case "nchar": csType = "string"; break;
                case "ntext": csType = "string"; break;
                case "nvarchar": csType = "string"; break;
                case "smalldatetime": csType = "DateTime"; break;
                case "smallint": csType = "short"; break;
                case "smallmoney": csType = "decimal"; break;
                case "text": csType = "string"; break;
                case "time": csType = "TimeSpan"; break;
                case "varchar": csType = "string"; break;
                default: csType = "object"; break;
            }
            return csType;
        }
    }
}
